package com.lry.router;

import com.lry.protocol.RpcInvocation;
import com.lry.proxy.Invoker;
import com.lry.spi.LoadLevel;

import java.util.*;

@LoadLevel(name="hash")
public class HashRouter implements Router{

    private TreeMap<Integer,String> VNode;
    private int V_NODE = 160;

    @Override
    public Invoker select(List<Invoker> invokers, RpcInvocation rpcInvocation) {
        if(null==VNode){
            VNode = new TreeMap<>();
            for(Invoker invoker:invokers){
                String ip = invoker.getIps();
                for(int i=0;i<V_NODE;i++){
                    VNode.put(getHash(ip+"VN"+i),ip);
                }
            }
        }else{
            handleNodeChange(invokers);
        }

        String ip = getIp(rpcInvocation.toString());
        for (Invoker invoker : invokers) {
            if(ip.equals(invoker.getIps())){
                return invoker;
            }
        }

        return null;
    }


    private void handleNodeChange(List<Invoker> invokers) {
        Iterator<Map.Entry<Integer, String>> it = VNode.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Integer, String> next = it.next();
            boolean f = false;
            for (Invoker invoker : invokers) {
                if(invoker.getIps().equals(next.getValue())){
                    f = true;
                    break;
                }
            }
            if(!f){
                it.remove();
            }
        }


        for (Invoker invoker : invokers) {
            boolean f = true;
            for (Map.Entry<Integer, String> entry : VNode.entrySet()) {
                if(invoker.getIps().equals(entry.getValue())){
                    f = false;
                    break;
                }
            }
            if(f){
                String ip = invoker.getIps();
                for(int i=0;i<V_NODE;i++){
                    VNode.put(getHash(ip+"VN"+i),ip);
                }
            }
        }

    }


    private String getIp(String param){
        int hash = getHash(param);
        //拿到比hash大的第一个节点
        Integer nodeIndex = VNode.tailMap(hash).firstKey();
        //节点不存在
        if(null==nodeIndex){
            //拿所有节点第一个
            nodeIndex = VNode.firstKey();
        }
        return VNode.get(nodeIndex);
    }

    private static int getHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++){
            hash = (hash ^ str.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        if (hash < 0){
            hash = Math.abs(hash);
        }
        return hash;
    }
}
